package org.labCompiladores.atividade1;

public class Interpretador{
	
	//Tabela de memória do programa representada por uma lista-ligada com 3 campos
	public class Table{
		String id;
		int value;
		Table tail;
		Table(String i, int v, Table t){
			id=i;
			value=v;
			tail=t;
		}
		
		int lookup(String key){ //Busca o valor da variável "key" na lista memória
			if(this.id == key){
				return this.value;
			}
			else{
				return tail.lookup(key);
			}
		}
	}
	
	//Representa o retorno de interpretação de expressions, já que estas podem
	//retornar um inteiro e ainda assim geram modificações na "memória" do programa
	public class IntAndTable{
		int i;
		Table t;
		IntAndTable(int ii, Table tt){
			i=ii;
			t=tt;
		}
	}
	
	
	//Busca o valor da variável "key" na lista memória e o retorna
	//sendo que o primeiro valor encontrado é o ultimo adicionado e, portanto
	//o válido
	int lookup(Table t, String key){
		return t.lookup(key);
	}
	
	
	//Adiciona novo valor "value" na id "key" na lista memória do programa
	Table update(Table t1, String key, int value){ //
		return new Table(key, value, t1);
	}
	
	//Interpreta Statements
	Table interpStm(Stm s, Table t){
		//Interpreta compound statements, executando e interpretando o stm1 e depois o stm2
		if (s instanceof CompoundStm) {
			CompoundStm cs = (CompoundStm) s;
			return interpStm(cs.stm2, interpStm(cs.stm1, t));
		}
		//Interpreta assign statements, executando a expression e depois o assign
		else if(s instanceof AssignStm) {
			AssignStm as = (AssignStm) s;
			IntAndTable iat = interpExp(as.exp,t);
			return update(iat.t,as.id,iat.i);
		}
		//Interpreta print statements, executando a lista de expressions e depois imprimindo seus resultados
		else{
			PrintStm ps = (PrintStm) s;
			IntAndTable iat = new IntAndTable(0,t);
			if(ps.exps instanceof PairExpList){ //Caso haja mais de um exp a ser executado devemos interpretar toda a lista deles
				PairExpList pel = (PairExpList) ps.exps;
				while((pel.tail instanceof LastExpList) != true){ //Enquanto cauda da lista de expressions não for o ultimo elemento
					iat = interpExp(pel.head, iat.t);//interpretar expressão da cabeça
					System.out.print(iat.i);//imprimir valor da expressão da cabeça
					System.out.print(" ");
					pel = (PairExpList) pel.tail;//andar na lista de expressões
				}
				iat = interpExp (pel.head, iat.t); //quando encontrar o ultimo elemento deve executar e imprimir o penultimo e depois o ultimo
				System.out.print(iat.i);
				System.out.print(" ");
				LastExpList lel = (LastExpList) pel.tail;//ultimo elemento da lista de expressões
				iat = interpExp(lel.head, iat.t);//interpretar ultimo elemento da lista de expressões
				System.out.print(iat.i);//imprimir resultado de ultimo elemento
				System.out.print("\n");
				return iat.t;//retornar tabela atualizada
			}
			else{//caso só haja um elemento na lista de expressões a serem computadas e impressas
				LastExpList lel = (LastExpList) ps.exps; 
				iat = interpExp(lel.head, iat.t);
				System.out.print(iat.i);
				System.out.print("\n");
				return iat.t;
			}
			
		}
	}
	
	//Interpreta Expressions
	IntAndTable interpExp(Exp e, Table t){
		//Caso expression seja uma Id expression deve retornar a mesma tabela e o valor da id correspondente
		if(e instanceof IdExp){
			IdExp ie = (IdExp) e;
			return new IntAndTable(lookup(t,ie.id),t);
		}
		//Caso se trate de uma NumExp deve-se retornar seu valor em conjunto com a tabela original t fornecida a ela
		else if(e instanceof NumExp){
			NumExp ne = (NumExp) e;
			return new IntAndTable(ne.num,t);
		}
		//Caso a expressão a ser interpretada seja uma expressão de operação
		else if(e instanceof OpExp){
			OpExp oe = (OpExp) e;
			IntAndTable iatl = interpExp(oe.left,t);//interpreta a expressão à esquerda
			IntAndTable iatr = interpExp(oe.right,iatl.t);//interpreta a expressão à direita utilizando a tabela modificada pela esquerda
			int res = iatl.i;
			switch(oe.oper){
			case 1://Plus
				res = res + iatr.i;
				break;
			case 2://Minus
				res = res - iatr.i;
				break;
			case 3://Times
				res = res * iatr.i;
				break;
			case 4://Div
				res = res / iatr.i;
				break;
			}
			
			return new IntAndTable(res, iatr.t);
		}
		//Caso a expressão a ser interpretada seja do tipo EseqExp
		else{
			EseqExp ee = (EseqExp) e;
			Table t1 = interpStm(ee.stm,t);//executa o statement primeiro
			return interpExp(ee.exp,t1);//executa a expressão em cima da tabela modificada pelo statement e retorna seu resultado
		}
	}
}	